#pragma once

#include <AVUIParallelTimeline.h>
#include <AVUIUncommonField.h>
#include <AVUIItemStructList.h>
#include <AVUIItemStructMap.h>

namespace AVUI {

class String;
class PropertyPath;
class UIElement;
class DependencyObject;
class Clock;
class AnimationClock;
class FrameworkTemplate;
class MediaClock;

class Storyboard : public ParallelTimeline
{
    friend class ChangeListener;
    class CloneCacheEntry;
public:
    DECLARE_ELEMENT(Storyboard, ParallelTimeline);

    DECLARE_DEPENDENCY_PROPERTY(TargetName);
    DECLARE_DEPENDENCY_PROPERTY(TargetProperty);

    Storyboard() { };

    struct Layers
    {
        static const int Code = 1;
        static const int ElementEventTrigger = 1;
        static const int PropertyTriggerStartLayer = 2;
    };

    struct ObjectPropertyPair
    {
        ObjectPropertyPair() : pProperty(NULL) { }
        ObjectPropertyPair(DependencyObject* pDO, DependencyProperty* pProperty) : pDO(pDO), pProperty(pProperty) { }
        DependencyObjectPtr pDO;
        DependencyProperty* pProperty;
    };

    struct ClockMapping
    {
        ObjectPropertyPair m_key;
        ObjectPtr m_pData;
    };

    typedef ItemStructList<ClockMapping> ClockMappingList;

    void Begin(DependencyObject* pContainingObject, FrameworkTemplate* pTemplate, HandoffBehavior::Enum handoffBehavior = HandoffBehavior::SnapshotAndReplace, bool isControllable = false);
    void Begin(DependencyObject* pContainingObject, HandoffBehavior::Enum handoffBehavior = HandoffBehavior::SnapshotAndReplace, bool isControllable = false)
    {
        BeginCommon(pContainingObject,NULL,handoffBehavior,isControllable, Layers::Code);
    }
    void BeginCommon(DependencyObject* pContainingObject, FrameworkTemplate* pTemplate, HandoffBehavior::Enum handoffBehavior, bool isControllable, int layer);


    static void GetComplexPathValue(DependencyObject* pTargetObject, DependencyProperty* pTargetProperty, EffectiveValueEntry* pEntry, PropertyMetadata* pMetadata);

protected:
    virtual TRefCountedPtr<Freezable> CreateInstanceCore() AVUI_OVERRIDE { return object_allocate<Storyboard>().get_Ptr(); }

private:
    static void StaticConstructor();

    static void CloneEffectiveValue(EffectiveValueEntry* pEntry, CloneCacheEntry* pCacheEntry);

    class ChangeListener : public Object
    {
    public:
        ChangeListener(DependencyObject* pTarget, Freezable* pClone, DependencyProperty* pProperty, Freezable* pOriginal);

    private:
        void InvalidatePropertyOnCloneChange(Object* pSender, EventArgs* pEventArgs);
        void InvalidatePropertyOnOriginalChange(Object* pSender, EventArgs* pEventArgs);
        void Setup();
        void Cleanup();

        Freezable* m_pOriginal;
        Freezable* m_pClone;
        DependencyProperty* m_pProperty;
        DependencyObject* m_pTarget;
    };

    class CloneCacheEntry : public Object
    {
    public:
        CloneCacheEntry() { };
        CloneCacheEntry(Object* pSource, Object* pClone) : pSource(pSource), pClone(pClone) { };
        ObjectPtr pClone;
        ObjectPtr pSource;
        TRefCountedPtr<ChangeListener> pChangeListener;
    };

    typedef ItemStructList<TRefCountedPtr<AnimationClock> > AnimationClockList;


    void ClockTreeWalkRecursive(Clock* pClock, DependencyObject* pContainingObject, FrameworkTemplate* pTemplate, String* pParentObjectName, PropertyPath* pParentPropertyPath, HandoffBehavior::Enum, ClockMappingList* pMappings, int layer);
    void ApplyAnimationClocks(ClockMappingList* pMappings, HandoffBehavior::Enum handoffBehavior, int layer);
    void SetStoryboardClock(DependencyObject* pContainingObject, Clock* pClock);
    static DependencyObject* ResolveTargetName(String* pTargetName, FrameworkTemplate* pTemplate, DependencyObject* pContainingElement);
    static void VerifyAnimationIsValid(DependencyProperty* pTargetProperty, AnimationClock* pAnimationClock);
    static void VerifyPathIsAnimatable(PropertyPath* pPath);
    static void VerifyComplexPathSupport(Object* pObject);
    static void UpdateMappings(ClockMappingList* pMappings, const ObjectPropertyPair& mappingKey, AnimationClock* pAnimationClock);
    static ClockMapping* FindMapping(ClockMappingList* pMappings, const ObjectPropertyPair& mappingKey);
    static CloneCacheEntry* GetComplexPathClone(DependencyObject* pDO, DependencyProperty* pDP);
    void ProcessComplexPath(ClockMappingList* pMappings, DependencyObject* pTargetObject, PropertyPath* pPath, AnimationClock* pAnimationClock, HandoffBehavior::Enum handoffBehavior, long layer);
    void ListenToChangesOnFreezable(DependencyObject* pTargetObject, Freezable* pClone, DependencyProperty* pProperty, Freezable* pCurrentValue);
    static bool IsPropertyCloningRequired(DependencyObject* pTargetObject, Object* pCurrentValue, DependencyProperty* pDP);
    static void SetComplexPathClone(DependencyObject* pDO, DependencyProperty* pDP, Object* pSource, Object* pClone);
    static void ApplyMediaClock(FrameworkTemplate* pTemplate, DependencyObject* pContainingObject, String* pTargetName, MediaClock* pClock);

    struct StoryboardClockTreeItem
    {
        StoryboardClockTreeItem() : pStoryboard(NULL) { }
        StoryboardClockTreeItem(Storyboard* pStoryboard, Clock* pClock) : pStoryboard(pStoryboard), pClock(pClock) { }
        Storyboard* pStoryboard;
        TWeakPtr<Clock> pClock;
    };
    typedef ItemStructList<StoryboardClockTreeItem> StoryboardClockTreeItemList;

    static UncommonField<StoryboardClockTreeItemList> StoryboardClockTreesField;  
    static UncommonField<FrugalMap> ComplexPathCloneField;  
};


}; // namespace AVUI
